package com.scau.os.course_manager;

import com.scau.os.course_manager.Clock.TimeManager;
import com.scau.os.course_manager.bean.Address;
import com.scau.os.course_manager.bean.DeviceAllocationTable;
import com.scau.os.course_manager.bean.PCB;
import com.scau.os.course_manager.bean.ProcessState;
import com.scau.os.deviceManagement.DeviceExecute;
import com.scau.os.extension.EventSystem;
import com.scau.os.extension.MyDelegate;

import java.security.SecureRandom;
import java.util.List;

public class Scheduler {

    private PCB idlePCB=ProcessPool.getInstance().idle;
    private ProcessPool processPool=ProcessPool.getInstance();
    private MemoryManager memoryManager=new MemoryManager();
    private DeviceManager deviceManager =new DeviceManager();
    private EmulationCPU emulationCPU=new EmulationCPU();

    public Scheduler() {
        register();

        idlePCB.setState(ProcessState.RUNNING);
        emulationCPU.setPcb(idlePCB);
    }

    /**
     * 所有进程的分配都在这里进行，包括闲逛进程
     * @return
     */
    public PCB pickPCB(){
        List<PCB> ready=getReady();
        SecureRandom random = new SecureRandom();

        if(ready.size()==0){
            idlePCB.setState(ProcessState.RUNNING);
            return idlePCB;
        }else {
            PCB pcb=null;
            pcb=ready.get(0);
            ready.remove(pcb);
            pcb.setState(ProcessState.RUNNING);
            return pcb;

        }

    }
    public void blockProcess(PCB pcb){
        List<PCB> block=getBlock();
        pcb.setState(ProcessState.BLOCK);
        pcb.blockEvent="IOTrap";
        block.add(pcb);
    }

    public void awakeProcess(PCB pcb){
        List<PCB> block=getBlock();
        List<PCB> ready=getReady();
        pcb.setState(ProcessState.READY);
        pcb.blockEvent="";
        block.remove(pcb);
        ready.add(pcb);
    }

    public void switchPCB(){

    }
    public PCB createProcess(int size,List<Byte> instructions){
        List<PCB> block=getBlock();
        List<PCB> ready=getReady();
        List<PCB> blank=getBlank();

        //申请主存空间
        if(!memoryManager.apply(blank.get(0), size,instructions)){
            return null;
        }
        //申请空白进程控制块
        if(blank.size()>0)
        {
            PCB pcb=blank.get(0);
            blank.remove(0);
            ready.add(pcb);
            //初始化进程控制块

            pcb.setState(ProcessState.READY);
            pcb.size=size;
            pcb.register.address=new Address(instructions) ;

            return pcb;
        }
        else {
            System.out.println("Process Count is Max");
            return null;
        }
    }
    public void destroyProcess(PCB pcb){
        List<PCB> blank=getBlank();
        pcb.setState(ProcessState.EXIT);
        //回收进程所占内存
        memoryManager.release(pcb);
        //回收进程控制块
        blank.add(pcb);
    }

    private void register(){

        //每隔一秒钟执行一次CPU的指令
        TimeManager.getInstance().addDelayTime(()->{},0,1,()->{
            emulationCPU.CPU();
        });

        //时间片到，切换进程
        EventSystem.instance.register(new MyDelegate("timeSlideOver") {
            @Override
            public void execute(Object data) {
                if(emulationCPU.getPcb().isIdle==false){
                    //维护当前PCB的状态
                    emulationCPU.getPcb().setState(ProcessState.READY);
                    getReady().add(emulationCPU.getPcb());
                }
                //修改正在运行的PCB
                emulationCPU.setPcb(pickPCB());
            }
        }).UnRegisterWhenClose(this);

        //执行到申请设备语句
        EventSystem.instance.register(new MyDelegate("applyDevice") {
            @Override
            public void execute(Object data) {
                DeviceAllocationTable deviceAllocationTable=(DeviceAllocationTable)data;
                deviceManager.apply(deviceAllocationTable);

                //维护当前PCB的状态
                blockProcess(deviceAllocationTable.pcb);
                //修改正在运行的PCB
                emulationCPU.setPcb(pickPCB());
            }
        }).UnRegisterWhenClose(this);

        //IO阻塞完成，解除进程阻塞
        //等到这个进程使用的设备时间到，解除这个进程的阻塞状态（发送一个进程唤醒事件）
        EventSystem.instance.register(new MyDelegate("awakeProcess") {
            @Override
            public void execute(Object data) {
                DeviceAllocationTable deviceAllocationTable=(DeviceAllocationTable)data;
                PCB pcb=deviceAllocationTable.pcb;
                if(pcb==null){
                    System.out.println("PCB is null!");
                }
                //维护当前PCB的状态
                awakeProcess(pcb);
                synchronized (emulationCPU){
                    if(emulationCPU.getPcb()==null){
                        System.out.println("emulationCPU.getPcb() is null!");
                    }
                    if(emulationCPU.getPcb().isIdle){

                        //修改正在运行的PCB
                        emulationCPU.setPcb(pickPCB());
                    }
                }
                if(deviceAllocationTable.peripheralName.length()==2){
                    //判断阻塞队列里面是否还有进程，那就为这个进程申请设备使用
                    deviceManager.checkPeripheralList(deviceAllocationTable.peripheralName.substring(0,1));
                }else {
                    System.err.println("非法的设备分配名");
                }

            }
        }).UnRegisterWhenClose(this);

        //通过命令行执行文件
        EventSystem.instance.register(new MyDelegate("executeStart") {
            @Override
            public void execute(Object data) {
                List<Byte>instructions=(List<Byte>)data;
                //维护当前PCB的状态
                PCB pcb=createProcess(instructions.size(),instructions);
                synchronized (emulationCPU){
                    if(emulationCPU.getPcb().isIdle){
                        //修改正在运行的PCB
                        emulationCPU.setPcb(pickPCB());
                    }
                }
                if(pcb==null){
                    System.out.println("文件执行失败");
                    EventSystem.instance.SendEvent("ShowDialog","文件执行失败");
                }
            }
        }).UnRegisterWhenClose(this);

        //进程执行完毕，回收它的内存和PCB
        EventSystem.instance.register(new MyDelegate("executeFinish") {
            @Override
            public void execute(Object data) {
                synchronized (emulationCPU){
                    //维护当前PCB的状态
                    destroyProcess(emulationCPU.getPcb());
                    //修改正在运行的PCB
                    emulationCPU.setPcb(pickPCB());
                }
            }
        }).UnRegisterWhenClose(this);
    }
    private List<PCB> getReady(){
        return processPool.ready;
    }
    private List<PCB> getBlock(){
        return processPool.block;
    }
    private List<PCB> getBlank(){
        return processPool.blank;
    }
}
